Creating and Using StatelessWidget in Flutter
StatelessWidget is one of the most important concepts in Flutter. It is used to create UI components whose configuration does not require mutable internal state. A StatelessWidget receives data through its constructor and describes the UI that should be displayed.
In Flutter, the user interface is built using widgets. StatelessWidget is especially useful for reusable UI components such as text sections, icons, buttons with fixed configuration, profile cards, headers, product cards, and other UI elements that do not need to maintain changing state.
For additional Flutter learning and training resources, visit JustAcademy Flutter Training and Register for a Course Demo.
1. What is StatelessWidget?
A StatelessWidget is a Flutter widget that does not require mutable state. Its UI is primarily determined by the values provided to the widget and the current BuildContext.
According to the Flutter API, a StatelessWidget describes part of the user interface by building other widgets. Flutter continues building the widget tree until the UI is represented by concrete rendering widgets.
Official reference: Flutter StatelessWidget API.
Basic Syntax
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return const Text('Hello Flutter');
}
}
The build() method returns the widgets that represent the UI.
2. Why Use StatelessWidget?
StatelessWidget is useful when a UI component does not need to change its own internal data during its lifetime.
- Creating reusable UI components
- Displaying static content
- Displaying data received through constructor parameters
- Creating layouts using Row, Column, Container, Padding, and other widgets
- Creating reusable cards and sections
- Building headers and navigation elements
- Creating presentation-only components
- Keeping UI code modular and maintainable
3. Structure of a StatelessWidget
A typical StatelessWidget contains three important parts:
- The class extends
StatelessWidget.
- A constructor is usually provided.
- The
build() method returns a widget tree.
class WelcomeMessage extends StatelessWidget {
const WelcomeMessage({super.key});
@override
Widget build(BuildContext context) {
return const Text(
'Welcome to Flutter',
);
}
}
Explanation
extends StatelessWidget tells Flutter that the class is a stateless widget.
const WelcomeMessage({super.key}) defines the constructor.
build() describes what should appear on the screen.
Text() displays the message.
4. Understanding the build() Method
The build() method is responsible for describing the widget's UI.
@override
Widget build(BuildContext context) {
return const Text('Hello World');
}
The BuildContext provides information about the widget's location in the widget tree and can be used to access inherited information such as theme, media information, localization, and navigation.
Example
class Greeting extends StatelessWidget {
const Greeting({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text(
'Good Morning!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
);
}
}
5. Creating Your First StatelessWidget
Let's create a simple application using a custom StatelessWidget.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('StatelessWidget Example'),
),
body: const Center(
child: Text(
'Hello Flutter!',
style: TextStyle(fontSize: 24),
),
),
),
);
}
}
How It Works
- The
main() function is the entry point.
runApp() starts the Flutter application.
MyApp extends StatelessWidget.
- The
build() method returns a MaterialApp.
MaterialApp contains a Scaffold.
- The Scaffold contains an AppBar and body.
- The body displays a Text widget.
6. StatelessWidget with Constructor Parameters
StatelessWidgets become more useful when they receive data through constructor parameters.
class UserCard extends StatelessWidget {
final String name;
final String email;
const UserCard({
super.key,
required this.name,
required this.email,
});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(email),
],
),
),
);
}
}
Using the Widget
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Padding(
padding: EdgeInsets.all(16),
child: UserCard(
name: 'Rahul Sharma',
email: '[email protected]',
),
),
);
}
}
Here, name and email are immutable properties of the widget.
7. Why Use final Variables?
Properties of a StatelessWidget are normally declared using final.
class ProductCard extends StatelessWidget {
final String productName;
final double price;
const ProductCard({
super.key,
required this.productName,
required this.price,
});
@override
Widget build(BuildContext context) {
return Text('$productName - ₹$price');
}
}
Using final makes the properties immutable after the object is created. This matches the immutable nature of widgets.
8. StatelessWidget with Container
class ColoredBoxWidget extends StatelessWidget {
const ColoredBoxWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 100,
color: Colors.blue,
alignment: Alignment.center,
child: const Text(
'Flutter',
style: TextStyle(
color: Colors.white,
fontSize: 22,
),
),
);
}
}
This widget creates a fixed visual component containing a blue box and text.
9. StatelessWidget with Row
class UserInfo extends StatelessWidget {
const UserInfo({super.key});
@override
Widget build(BuildContext context) {
return const Row(
children: [
CircleAvatar(
child: Icon(Icons.person),
),
SizedBox(width: 12),
Text(
'John Doe',
style: TextStyle(fontSize: 18),
),
],
);
}
}
This is a good example of creating a reusable horizontal UI component.
10. StatelessWidget with Column
class ProfileSection extends StatelessWidget {
const ProfileSection({super.key});
@override
Widget build(BuildContext context) {
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'John Doe',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text('Flutter Developer'),
Text('Mumbai, India'),
],
);
}
}
11. Creating a Reusable Button Widget
class CustomButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
const CustomButton({
super.key,
required this.title,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(title),
);
}
}
Using CustomButton
CustomButton(
title: 'Login',
onPressed: () {
print('Login clicked');
},
)
The button itself is stateless, while the action to perform is provided through the callback.
12. StatelessWidget and Callbacks
A StatelessWidget can receive a callback from its parent. This allows the widget to communicate an event without storing mutable state itself.
class ActionButton extends StatelessWidget {
final VoidCallback onTap;
const ActionButton({
super.key,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onTap,
child: const Text('Click Me'),
);
}
}
Parent Usage
ActionButton(
onTap: () {
print('Button clicked');
},
)
13. StatelessWidget and Lists
StatelessWidgets are commonly used to display list items.
class ProductItem extends StatelessWidget {
final String name;
final double price;
const ProductItem({
super.key,
required this.name,
required this.price,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: const Icon(Icons.shopping_bag),
title: Text(name),
subtitle: Text('₹$price'),
trailing: const Icon(Icons.arrow_forward_ios),
);
}
}
Example:
Column(
children: const [
ProductItem(
name: 'Laptop',
price: 55000,
),
ProductItem(
name: 'Keyboard',
price: 1500,
),
ProductItem(
name: 'Mouse',
price: 800,
),
],
)
14. StatelessWidget for a Profile Card
class ProfileCard extends StatelessWidget {
final String name;
final String role;
final String location;
const ProfileCard({
super.key,
required this.name,
required this.role,
required this.location,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const CircleAvatar(
radius: 30,
child: Icon(Icons.person),
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(role),
Text(location),
],
),
],
),
),
);
}
}
15. StatelessWidget and Conditional UI
A StatelessWidget can display different UI based on constructor values. The widget itself does not change those values.
class LoginStatus extends StatelessWidget {
final bool isLoggedIn;
const LoginStatus({
super.key,
required this.isLoggedIn,
});
@override
Widget build(BuildContext context) {
return Text(
isLoggedIn ? 'Welcome Back!' : 'Please Login',
);
}
}
Usage
const LoginStatus(isLoggedIn: true)
The parent can provide a different value when the widget is rebuilt.
16. StatelessWidget vs StatefulWidget
| Feature | StatelessWidget | StatefulWidget |
| Mutable internal state | Not required | Supported |
| State object | No separate State object | Uses a separate State object |
| UI changes | Based on configuration/context and rebuilds | Can change through state updates |
| setState() | Not available directly | Available in State |
| Typical use | Reusable/static or configuration-driven UI | Interactive or changing UI |
| Example | Profile card | Counter |
17. StatelessWidget Does Not Mean the Entire Screen Can Never Change
An important concept is that a StatelessWidget does not mean the screen can never change.
A StatelessWidget can rebuild when its parent provides a new configuration or when dependencies it uses change. The important point is that the widget itself does not maintain mutable internal state.
class Greeting extends StatelessWidget {
final String name;
const Greeting({
super.key,
required this.name,
});
@override
Widget build(BuildContext context) {
return Text('Hello, $name');
}
}
If the parent rebuilds and provides a different name, the displayed text can change.
18. Using ThemeData in StatelessWidget
StatelessWidgets can access application theme information through BuildContext.
class ThemedTitle extends StatelessWidget {
const ThemedTitle({super.key});
@override
Widget build(BuildContext context) {
return Text(
'Flutter Course',
style: Theme.of(context).textTheme.headlineMedium,
);
}
}
This allows the widget to use the application's current theme instead of hard-coding every style.
19. Using MediaQuery in StatelessWidget
A StatelessWidget can also use screen information from the current context.
class ResponsiveText extends StatelessWidget {
const ResponsiveText({super.key});
@override
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
return Text(
width > 600 ? 'Large Screen' : 'Small Screen',
);
}
}
This demonstrates that StatelessWidget can still create responsive interfaces without maintaining mutable state.
20. Using LayoutBuilder
LayoutBuilder can be used when a widget needs to adapt its layout according to the constraints supplied by its parent.
class ResponsiveBox extends StatelessWidget {
const ResponsiveBox({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return const Text(
'Desktop Layout',
style: TextStyle(fontSize: 30),
);
}
return const Text(
'Mobile Layout',
style: TextStyle(fontSize: 20),
);
},
);
}
}
21. Using const with StatelessWidget
Flutter recommends using const widgets wherever possible. A StatelessWidget can provide a const constructor when its fields can be initialized as compile-time constants.
class Welcome extends StatelessWidget {
const Welcome({super.key});
@override
Widget build(BuildContext context) {
return const Text('Welcome to Flutter');
}
}
Then it can be created as:
const Welcome()
Using const can help Flutter avoid unnecessary widget work when the configuration is unchanged.
22. Example: Product Card
class ProductCard extends StatelessWidget {
final String title;
final String imageUrl;
final double price;
const ProductCard({
super.key,
required this.title,
required this.imageUrl,
required this.price,
});
@override
Widget build(BuildContext context) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
imageUrl,
height: 180,
width: double.infinity,
fit: BoxFit.cover,
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 6),
Text(
'₹$price',
style: const TextStyle(
fontSize: 16,
),
),
],
),
),
],
),
);
}
}
23. Example: Complete Stateless Flutter Application
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'StatelessWidget Demo',
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My Flutter App'),
),
body: const Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
ProfileCard(
name: 'Amit Kumar',
role: 'Flutter Developer',
location: 'Delhi, India',
),
SizedBox(height: 20),
Text(
'Welcome to Flutter Development',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
class ProfileCard extends StatelessWidget {
final String name;
final String role;
final String location;
const ProfileCard({
super.key,
required this.name,
required this.role,
required this.location,
});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const CircleAvatar(
radius: 30,
child: Icon(Icons.person),
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(role),
Text(location),
],
),
],
),
),
);
}
}
24. Understanding the Widget Tree
Flutter applications are constructed as a tree of widgets. A StatelessWidget can contain many child widgets.
MaterialApp
└── Scaffold
├── AppBar
│ └── Text
└── Body
└── Column
├── ProfileCard
│ ├── CircleAvatar
│ └── Text
└── Text
Each widget describes a part of the user interface, and parent widgets can contain child widgets.
25. StatelessWidget and Reusable Components
One of the major advantages of StatelessWidget is component reusability.
For example, instead of writing the same product card UI multiple times, create one reusable ProductCard widget.
ProductCard(
title: 'Laptop',
imageUrl: 'https://example.com/laptop.jpg',
price: 55000,
)
ProductCard(
title: 'Mobile',
imageUrl: 'https://example.com/mobile.jpg',
price: 25000,
)
The same widget structure can display different information through constructor parameters.
26. Common Use Cases
- Home page layouts
- Profile cards
- Product cards
- List items
- Headers
- Footers
- Static information sections
- Reusable buttons
- Icons and labels
- Dashboard cards
- Information banners
- Empty-state screens
- Reusable form sections
- Presentation-only components
27. Common Mistakes
Mistake 1: Trying to Use setState()
class Example extends StatelessWidget {
int count = 0;
void increment() {
setState(() {
count++;
});
}
}
This is incorrect because setState() belongs to the State object of a StatefulWidget.
Correct Approach
If the widget needs to manage changing internal state, consider using StatefulWidget.
Mistake 2: Using Mutable Fields
class Example extends StatelessWidget {
String name = 'John';
}
For a StatelessWidget, widget configuration should generally be immutable.
Prefer:
class Example extends StatelessWidget {
final String name;
const Example({
super.key,
required this.name,
});
}
Mistake 3: Putting Too Much Logic Inside build()
The build method should primarily describe the UI. Avoid unnecessarily expensive operations inside it.
Mistake 4: Creating Reusable UI as Large Helper Methods
For reusable pieces of UI, creating a dedicated widget can improve organization and make Flutter's widget tree easier to reason about.
28. StatelessWidget Best Practices
- Use
final for widget properties.
- Use
const constructors whenever appropriate.
- Keep widgets small and focused.
- Use meaningful widget names.
- Pass required data through constructors.
- Use callbacks for user actions when appropriate.
- Avoid unnecessary work inside
build().
- Extract reusable UI into separate widgets.
- Keep business logic separate from presentation where appropriate.
- Use Flutter's built-in widgets where they provide the required functionality.
29. When Should You Use StatelessWidget?
Use StatelessWidget when the component does not need to maintain mutable internal state.
Good Examples
- A logo
- A heading
- A product information card
- A profile display card
- A static menu item
- A reusable button configuration
- A decorative UI component
- A layout section
Consider StatefulWidget When
- A value changes inside the widget.
- The widget needs to call
setState().
- The widget manages a text editing controller.
- The widget maintains a selected item.
- The widget manages an animation controller.
- The widget has temporary interactive state.
- The UI depends on mutable state owned by that component.
30. StatelessWidget and State Management
StatelessWidget does not mean that an application cannot have state. Application state can be managed elsewhere and passed into StatelessWidgets as constructor parameters.
class CounterDisplay extends StatelessWidget {
final int count;
const CounterDisplay({
super.key,
required this.count,
});
@override
Widget build(BuildContext context) {
return Text(
'Count: $count',
style: const TextStyle(fontSize: 24),
);
}
}
A parent or state-management solution can provide the current count. The CounterDisplay widget only displays the value it receives.
31. StatelessWidget in Real-World Applications
Large Flutter applications are normally composed of many small widgets. StatelessWidgets are useful for separating the interface into reusable components.
For example, an e-commerce application might contain:
HomePage
├── HeaderWidget
├── SearchBarWidget
├── CategoryWidget
├── ProductCard
├── ProductCard
├── ProductCard
└── BottomNavigationWidget
Individual components can be implemented as StatelessWidgets when they do not own mutable state.
32. Interview Questions
Q1. What is StatelessWidget?
StatelessWidget is a Flutter widget used when the widget does not require mutable internal state.
Q2. Which method is required when creating a StatelessWidget?
The build() method is overridden to describe the widget's UI.
Q3. Can a StatelessWidget receive changing data?
Yes. It can receive new values through constructor parameters when its parent rebuilds it with a new configuration.
Q4. Can StatelessWidget use setState()?
No. setState() is provided by the State object associated with StatefulWidget.
Q5. Why are StatelessWidget properties usually final?
Widgets are immutable configuration objects, so properties are generally declared as final.
Q6. Can a StatelessWidget use callbacks?
Yes. A callback can be passed through the constructor to handle events such as button presses.
Q7. What is the purpose of BuildContext?
BuildContext identifies the widget's location in the widget tree and provides access to information available from its surrounding widget hierarchy.
Q8. Why use const with StatelessWidget?
A const widget can be created as a compile-time constant when its configuration allows it, helping Flutter reduce unnecessary work during rebuilds.
33. Practice Exercise
Create a reusable StudentCard StatelessWidget with the following properties:
- Student name
- Course name
- Email
- Phone number
- Profile icon
Requirements:
- Create a separate StatelessWidget called
StudentCard.
- Use constructor parameters.
- Declare the properties as
final.
- Use a
Card widget.
- Use
Row and Column.
- Add a CircleAvatar.
- Display all student information.
- Use a const constructor where possible.
Starter Code
class StudentCard extends StatelessWidget {
final String name;
final String course;
final String email;
final String phone;
const StudentCard({
super.key,
required this.name,
required this.course,
required this.email,
required this.phone,
});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const CircleAvatar(
child: Icon(Icons.person),
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name),
Text(course),
Text(email),
Text(phone),
],
),
],
),
),
);
}
}
34. Quick Revision
| Concept | Meaning |
| StatelessWidget | Widget that does not require mutable internal state |
| build() | Method used to describe the UI |
| BuildContext | Represents the widget's location in the widget tree |
| final | Used for immutable widget properties |
| const | Used for compile-time constant widget configurations when possible |
| Constructor | Used to receive configuration/data |
| Callback | Used to pass actions/events into a widget |
| StatefulWidget | Used when mutable state needs to be managed by a widget |
35. Key Takeaways
- StatelessWidget is used for UI that does not require mutable internal state.
- The
build() method describes the widget's UI.
- Constructor parameters are commonly used to provide data to a StatelessWidget.
- Widget properties are generally immutable and declared using
final.
- StatelessWidgets can contain other widgets and form a widget tree.
- StatelessWidgets can receive callbacks for handling events.
- StatelessWidgets can use BuildContext to access inherited information such as theme and layout information.
- Use
const constructors and widgets where appropriate.
- Reusable StatelessWidgets help create clean and maintainable Flutter applications.
- When a component needs to own mutable state, StatefulWidget may be more appropriate.
36. Learning Resources
Conclusion
StatelessWidget is a fundamental building block of Flutter applications. It is ideal for reusable UI components whose own configuration does not require mutable internal state. By combining constructor parameters, immutable properties, the build() method, callbacks, and other Flutter widgets, developers can create clean, reusable, and maintainable interfaces.
Understanding StatelessWidget is also an important foundation for learning StatefulWidget, widget trees, state management, responsive UI, and larger Flutter application architecture.
For structured Flutter learning, explore JustAcademy Flutter Training and book a course demo.